#include "scripting/script_lexer.h"
#include "scripting/script_header.h"
#include "utils/utils/string_util.h"
#include "utils/utils/string_cast.h"
#include <unordered_set>
#include <regex>

using namespace base::utils;

namespace script
{
class ScriptLexerData
{
private:
    friend class ScriptLexer;
    std::unordered_set<std::string> key_word{"var", "function", "if", "else", "repeat", "retry", "and", "or", "while",
                                             "reactive", "parallel", "force_fail", "force_pass"};
    std::unordered_set<char> delimiter{'(', ')', ' ', '"', '\n', ';', '#', '{', '}', '!', '='};  //分割符
    std::unordered_set<char> operator_delimiter{'(', ')', '"', '{', '}', '!', '='};   //操作符
    script::Tokens tokens;

    std::string error_message;
    int error_line{0};
    int error_column{0};

private:
    bool parserLineCode(const std::string &line_code, int line);
};

bool ScriptLexerData::parserLineCode(const std::string &line_code, int line)
{
    bool parser_res = true;
    std::string process_str;
    for (int index = 0; index < line_code.size(); index++)
    {
        char ch = line_code.at(index);
        if (delimiter.find(ch) == delimiter.end())
        {
            //不是token分割符，继续遍历
            process_str.push_back(ch);
            continue;
        }
        //是token分割符，先处理前面得字符串
        if (process_str.empty())
        {
            //process_str为空，表示连续出现两个token分割符，则保存分割符即可
        }
        else if (key_word.find(process_str) != key_word.end())
        {
            //是关键字
            tokens.tokens.push_back(script::Token{script::TokenType::KeyWord, process_str, line, index + 1 - (int) process_str.length()});
        }
        else if (StringUtil::isDigit(process_str))
        {
            //数字常量
            tokens.tokens.push_back(script::Token{script::TokenType::Numeric, process_str, line, index + 1 - (int) process_str.length()});
        }
        else if (StringUtil::isIdentifier(process_str))
        {
            //标识符
            tokens.tokens.push_back(script::Token{script::TokenType::Identifier, process_str, line, index + 1 - (int) process_str.length()});
        }
        else
        {
            parser_res = false;
            error_line = line;
            error_column = index + 1 - (int) process_str.length();
            error_message = StringUtil::format("syntax error, unknown code: %s", process_str.c_str());
            break;
        }
        //再处理分割字符
        if ('#' == ch)
        {
            //注释，后面不再处理
            break;
        }
        if (operator_delimiter.find(ch) != operator_delimiter.end())
        {
            tokens.tokens.push_back(script::Token{script::TokenType::Punctuator, std::string(1, ch), line, index + 1});
        }
        process_str.clear();
    }
    return parser_res;
}
}

namespace script
{
ScriptLexer::ScriptLexer()
    : d(std::make_unique<ScriptLexerData>())
{

}
std::optional<script::Tokens> ScriptLexer::scan(const std::string &code)
{
    std::vector<std::string> line_code_vec = StringUtil::split(code, "\n");
    for (int index = 0; index < line_code_vec.size(); index++)
    {
        if (!d->parserLineCode(line_code_vec.at(index) + '\n', index))
        {
            return {};
        }
    }
    return d->tokens;
}
void ScriptLexer::getErrorInfo(int *line, int *column, std::string *error_msg) const
{
    *line = d->error_line;
    *column = d->error_column;
    *error_msg = d->error_message;
}
ScriptLexer::~ScriptLexer() = default;

}
